#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;

namespace Yarr
{
	/// <summary>
	/// This class represents cons nodes in the Yarr runtime.
    /// A Cons is a compound object having two components, called the car 
	/// and the cdr, each of which can be any object. Lists are created
	/// by setting the cdr to another cons. Trees are created by setting the car to
	/// another cons.
    /// All of the various list manipulation functions, such as car, cdr, cxxxr, etc. 
    /// are available directly as instance methods of Yarr.Cons so that hosting 
    /// applications can perform list manipulation directly, without having to 
    /// invoke the Yarr runtime.
	/// </summary>
    public class Cons : ICollection
    {
        #region Private members
        private Object car;
		private Object cdr;
        #endregion


        #region Static methods
        /// <summary>
        /// Factory to build a list from multiple arguments
        /// </summary>
        /// <param name="items">The items</param>
        /// <returns>A Cons node representing the items as a Yarr list</returns>
        public static Cons MakeCons(params object[] items)
        {
            Cons cons = null;
            for (int i = items.Length - 1; i >= 0; i--)
            {
                cons = new Cons(items[i], cons);
            }
            return cons;
        }
        #endregion


        #region Constructors
        /// <summary>
		/// Constructs a new cons node with one element
		/// </summary>
		/// <param name="car">The head of the cons node</param>
		public Cons(Object car) 
		{
			this.car = car;
		}

		/// <summary>
		/// Constructs a new cons node with two elements
		/// </summary>
		/// <param name="car">The head element of the cons node</param>
		/// <param name="cdr">The tail element of the cons nodet</param>
		public Cons(Object car, Object cdr)
		{
			this.car = car;
			this.cdr = cdr;
        }
        #endregion


        #region ICollection implementation
        /// <summary>
		/// Returns an enumerator for enumerating all elements of the List
		/// started by this cons.
		/// </summary>
		/// <returns></returns>
		public IEnumerator GetEnumerator() 
		{
			return (new ListEnumerator(this));
        }

        /// <summary>
        /// Synonym for Length
        /// </summary>
        /// <returns>The length of the list</returns>
        public int Count
        {
            get { return Length; }
        }

        /// <summary>
        /// Returns the length of the list
        /// </summary>
        /// <returns>the length of the list</returns>
        public int Length
        {
            get
            {
                int i = 0;
                object o = this;
                while (o != null)
                {
                    ++i;
                    o = ((Cons)o).Cdr();
                }
                return i;
            }
        }

        /// <summary>
        /// Implement ICollection.IsSynchronized
        /// </summary>
        public bool IsSynchronized
        {
            get { return false; }
        }

        /// <summary>
        /// Implement ICollection.SyncRoot
        /// </summary>
        public object SyncRoot
        {
            get { return this; }
        }
        #endregion


        #region C[a|d]R methods
        /// <summary>
		/// Returns the Car (First) of this cons
		/// </summary>
		/// <returns></returns>
        public Object Car() 
		{
			return this.car;
		}

        /// <summary>
        /// Sets the Car (First) of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCar (object o)
        {
            return this.car = o;
        }

		/// <summary>
		/// Returns the Cdr (Rest) of this cons
		/// </summary>
		/// <returns></returns>
		public Object Cdr() 
		{
			return this.cdr;
		}

        /// <summary>
        /// Sets the Cdr (Rest) of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCdr (object o)
        {
            return this.cdr = o;
        }

		/// <summary>
		/// Returns the Car of the Car of this cons 
		/// </summary>
		/// <returns></returns>
		public Object Caar() 
		{
            if (this.Car() == null)
                return null;

            if (!(this.Car() is Cons))
                throw new YarrException("Caar: Argument is not a Cons");

			return ((Cons)this.Car()).Car();
		}

        /// <summary>
        /// Sets the Car of the Car of this cons 
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCaar (object o)
        {
            if (this.Car() == null  ||  !(this.Car() is Cons))
                throw new YarrException("SetCaar: Argument is not a Cons");

            return ((Cons)this.Car()).SetCar(o);
        }

		/// <summary>
		/// Returns the Car of the Cdr of this cons
		/// </summary>
        /// <returns></returns>
		public Object Cadr() 
		{
            if (this.Cdr() == null)
                return null;

            if (!(this.Cdr() is Cons))
                throw new YarrException("Cadr: Argument is not a Cons");

			return ((Cons)this.Cdr()).Car();
		}

        /// <summary>
        /// Sets the Car of the Cdr of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCadr (object o)
        {
            if (this.Cdr() == null  ||   !(this.Cdr() is Cons))
                throw new YarrException("SetCadr: Argument is not a Cons");

            return ((Cons)this.Cdr()).SetCar(o);
        }

		/// <summary>
		/// Returns the Cdr of the Car of this cons
		/// </summary>
		/// <returns></returns>
		public Object Cdar() 
		{
            if (this.Car() == null)
                return null;

            if (!(this.Car() is Cons))
                throw new YarrException("Cdar: Argument is not a Cons");

            return ((Cons)this.Car()).Cdr();
		}

        /// <summary>
        /// Sets the Cdr of the Car of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCdar (object o)
        {
            if (this.Car() == null  ||  !(this.Car() is Cons))
                throw new YarrException("SetCdar: Argument is not a Cons");

            return ((Cons)this.Car()).SetCdr(o);
        }

		/// <summary>
		/// Returns the Cdr or the Cdr of this cons
		/// </summary>
        /// <returns></returns>
		public Object Cddr() 
		{
            if (this.Cdr() == null)
                return null;

            if (!(this.Cdr() is Cons))
                throw new YarrException("Cddr: Argument is not a Cons");

			return ((Cons)Cdr()).Cdr();
		}

        /// <summary>
        /// Sets the Cdr or the Cdr of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCddr (object o)
        {
            if (this.Cdr() == null  ||  !(this.Cdr() is Cons))
                throw new YarrException("SetCddr: Argument is not a Cons");

            return ((Cons)Cdr()).SetCdr(o);
        }

        /// <summary>
        /// Returns the Car of the Car of the Car of this cons
        /// </summary>
        /// <returns></returns>
		public Object Caaar() 
		{
            if (this.Caar() == null)
                return null;

            if (!(this.Caar() is Cons))
                throw new YarrException("Caaar: Argument is not a Cons");

			return ((Cons)this.Caar()).Car();
		}

        /// <summary>
        /// Returns the Car of the Car of the Car of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCaaar (object o)
        {
            if (this.Caar() == null  ||  !(this.Caar() is Cons))
                throw new YarrException("SetCaaar: Argument is not a Cons");

            return ((Cons)this.Caar()).SetCar(o);
        }

        /// <summary>
        /// Returns the Car of the Car of the Cdr of this cons
        /// </summary>
        /// <returns></returns>
		public Object Caadr() 
		{
            if (this.Cadr() == null)
                return null;

            if (!(this.Cadr() is Cons))
                throw new YarrException("Caadr: Argument is not a Cons");
                
			return ((Cons)this.Cadr()).Car();
		}

        /// <summary>
        /// Sets the Car of the Car of the Cdr of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCaadr (object o)
        {
            if (this.Cadr() == null  ||  !(this.Cadr() is Cons))
                throw new YarrException("SetCaadr: Argument is not a Cons");

            return ((Cons)this.Cadr()).SetCar(o);
        }

        /// <summary>
        /// Returns the Car of the Cdr of the Car of this cons
        /// </summary>
        /// <returns></returns>
		public Object Cadar() 
		{
            if (this.Cdar() == null)
                return null;

            if (!(this.Cdar() is Cons))
                throw new YarrException("Cadar: Argument is not a Cons");

			return ((Cons)this.Cdar()).Car();
		}

        /// <summary>
        /// Sets the Car of the Cdr of the Car of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCadar (object o)
        {
            if (this.Cdar() == null  ||  !(this.Cdar() is Cons))
                throw new YarrException("SetCadar: Argument is not a Cons");

            return ((Cons)this.Cdar()).SetCar(o);
        }

        /// <summary>
        /// Returns the Car of the Cdr of the Cdr of this cons
        /// </summary>
        /// <returns></returns>
        public Object Caddr () 
		{
            if (this.Cddr() == null)
                return null;

            if (!(this.Cddr() is Cons))
                throw new YarrException("Caddr: Argument is not a Cons");

			return ((Cons)this.Cddr()).Car();
		}

        /// <summary>
        /// Sets the Car of the Cdr of the Cdr of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCaddr (object o)
        {
            if (this.Cddr() == null  ||  !(this.Cddr() is Cons))
                throw new YarrException("SetCaddr: Argument is not a Cons");

            return ((Cons)this.Cddr()).SetCar(o);
        }

        /// <summary>
        /// Returns the Cdr of the Car of the Car of this cons
        /// </summary>
        /// <returns></returns>
        public Object Cdaar() 
		{
            if (this.Caar() == null)
                return null;

            if (!(this.Caar() is Cons))
                throw new YarrException("Caddr: Argument is not a Cons");

			return ((Cons)this.Caar()).Cdr();
		}

        /// <summary>
        /// Sets the Cdr of the Car of the Car of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCdaar (object o)
        {
            if (this.Caar() == null  ||  !(this.Caar() is Cons))
                throw new YarrException("SetCdaar: Argument is not a Cons");

            return ((Cons)this.Caar()).SetCdr(o);
        }

        /// <summary>
        /// Returns the Cdr of the Car of the Cdr of this cons
        /// </summary>
        /// <returns></returns>
		public Object Cdadr() 
		{
            if (this.Cadr() == null)
                return null;

            if (!(this.Cadr() is Cons))
                throw new YarrException("Cdadr: Argument is not a Cons");

			return ((Cons)this.Cadr()).Cdr();
		}

        /// <summary>
        /// Sets the Cdr of the Car of the Cdr of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCdadr (object o)
        {
            if (this.Cadr() == null  ||  !(this.Cadr() is Cons))
                throw new YarrException("SetCdadr: Argument is not a Cons");

            return ((Cons)this.Cadr()).SetCdr(o);
        }

        /// <summary>
        /// Returns the Cdr of the Cdr of the Car of this form
        /// </summary>
        /// <returns></returns>
		public Object Cddar() 
		{
            if (this.Cdar() == null)
                return null;

            if (!(this.Cdar() is Cons))
                throw new YarrException("Cddar: Argument is not a Cons");

			return ((Cons)this.Cdar()).Cdr();
		}

        /// <summary>
        /// Sets the Cdr of the Cdr of the Car of this form
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCddar (object o)
        {
            if (this.Cdar() == null  ||  !(this.Cdar() is Cons))
                throw new YarrException("SetCddar: Argument is not a Cons");

            return ((Cons)this.Cdar()).SetCdr(o);
        }

        /// <summary>
        /// Returns the Cdr of the Cdr of the Cdr of this cons
        /// </summary>
        /// <returns></returns>
		public Object Cdddr() 
		{
            if (this.Cddr() == null)
                return null;

            if (!(this.Cddr() is Cons))
                throw new YarrException("Cdddr: Argument is not a Cons");

			return ((Cons)this.Cddr()).Cdr();
		}

        /// <summary>
        /// Sets the Cdr of the Cdr of the Cdr of this cons
        /// </summary>
        /// <param name="o">The new value</param>
        /// <returns></returns>
        public Object SetCdddr (object o)
        {
            if (this.Cddr() == null  ||  !(this.Cddr() is Cons))
                throw new YarrException("SetCdddr: Argument is not a Cons");

            return ((Cons)this.Cddr()).SetCdr(o);
        }
        #endregion


        #region Public methods
        /// <summary>
        /// A synonym for Car
        /// </summary>
        /// <returns>The first element in the list</returns>
        public Object First()
        {
            return this.car;
        }

        /// <summary>
        /// A synonym for Cadr
        /// </summary>
        /// <returns>The second element in the list</returns>
        public Object Second()
        {
            return this.Cadr();
        }

        /// <summary>
        /// A synonym for Caddr
        /// </summary>
        /// <returns>The third element in the list</returns>
        public Object Third()
        {
            return this.Caddr();
        }

        /// <summary>
		/// Synonym for Cdr
		/// </summary>
		/// <returns></returns>
		public Object Rest() 
		{
			return this.cdr;
		}

        /// <summary>
        /// Returns the last item in a list
        /// </summary>
        /// <returns></returns>
        public object Last()
        {
            object o = this;
            while ((o is Cons) && (((Cons)o).Cdr() != null))
            {
                o = ((Cons)o).Cdr();
            }

            return o;
        }


        /// <summary>
        /// Returns a specific element of this cons
        /// </summary>
        /// <param name="n">The index (zero-based)</param>
        /// <returns>The element at the specified index location</returns>
		public Object Nth(int n) 
		{
			if (n == 0) 
                return Car();

            if (Cdr() == null)
                return null;

            if (!(Cdr() is Cons))
                return new YarrException(String.Format(CultureInfo.CurrentCulture, "nth: {0} is not a list", Cdr()));

            return ((Cons)Cdr()).Nth(n - 1);
		}

        /// <summary>
        /// Sets the Nth element of the list
        /// </summary>
        /// <param name="n">the index (zero-based)</param>
        /// <param name="o">the new value</param>
        /// <returns>The new value</returns>
        public Object SetNth (int n, object o)
        {
            if (n == 0)
                return SetCar(o);

            if (Cdr() == null  ||  !(Cdr() is Cons))
                return new YarrException(String.Format(CultureInfo.CurrentCulture, "SetNth: {0} is not a list", Cdr()));

            return ((Cons)Cdr()).SetNth(n - 1, o);
        }


        /// <summary>
        /// Sets the CAR of this cons node
        /// </summary>
        /// <param name="obj">The new value for the CAR</param>
        /// <returns>This cons node</returns>
		public Cons Rplaca(Object obj) 
		{
			this.car = obj;
			return this;
		}

        /// <summary>
        /// Sets the CDR of this cons node
        /// </summary>
        /// <param name="obj">The new value for the CDR</param>
        /// <returns>This cons node</returns>
		public Cons Rplacd(Object obj) 
		{
			this.cdr = obj;
			return this;
		}


        /// <summary>
        /// Returns a new list with the items reversed
        /// </summary>
        /// <returns></returns>
        public Cons Reverse()
        {
            object list = null;
            object o = this;
            while (o != null)
            {
                list = new Cons(((Cons)o).Car(), list);
                o = ((Cons)o).Cdr();
                if (o != null && !(o is Cons))
                    throw new YarrException("Reverse: argument is not a proper list");
            }
            return (Cons)list;
        }

        /// <summary>
        /// Destructively reverses the list
        /// </summary>
        /// <returns></returns>
        public Cons NReverse()
        {
            Cons o = this;
            Cons rev = null;

            while (o != null)
            {
                if (o.cdr != null && !(o.cdr is Cons))
                    throw new YarrException("Nreverse: argument is not a proper list");
                Cons next = (Cons)o.cdr;
                o.cdr = rev;
                rev = o;
                o = next;
            }

            return rev;
        }


        /// <summary>
        /// Makes a shallow copy of this cons node
        /// </summary>
        /// <returns></returns>
        public Cons Copy()
        {
            return new Cons(this.Car(), this.Cdr());
        }

        /// <summary>
        /// Returns a shallow copy of this list
        /// </summary>
        /// <returns></returns>
        public Cons CopyList()
        {
            Cons result;
            Cons dest;

            object nextObj = this;
            Cons nextCons = this;

            // Copy the first cons cell
            result = dest = nextCons.Copy();
            nextObj = nextCons.Cdr();

            // Copy succesive cells
            while (nextObj is Cons)
            {
                nextCons = (Cons)nextObj;
                dest.Rplacd(nextCons.Copy());
                dest = (Cons)dest.Cdr();
                nextObj = nextCons.Cdr();
            }

            return result;
        }

        /// <summary>
        /// Copy the elements of this list to an array, beginning at a sepcific index
        /// </summary>
        /// <param name="array">The array to copy to</param>
        /// <param name="index">The array index to start copying</param>
        public void CopyTo(Array array, int index)
        {
            if (array != null)
            {
                int i = 0;
                Cons o = this;

                while (o != null)
                {
                    if (i >= index)
                    {
                        array.SetValue(o.Car(), i);
                    }
                    i += 1;
                    o = (Cons)o.Cdr();
                }
            }
        }


        /// <summary>
        /// Override default Object.Equals()
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (obj is Cons)
            {
                Cons that = (Cons)obj;

                bool carsEqual = Primitives.Eql(this.Car(), that.Car());
                bool cdrsEqual = Primitives.Eql(this.Cdr(), that.Cdr());

                return carsEqual && cdrsEqual;

            }
            else
                return false;
        }

        /// <summary>
        /// Overrides the default ToString to return the string representation of the list
        /// as produced by the Yarr Printer.
        /// </summary>
        /// <returns>The string represention of the list.</returns>
        public override string ToString()
        {
            return Printer.WriteToString(this);
        }

        /// <summary>
        /// Overrides default Object.GetHashCode()
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return Printer.WriteToString(this).GetHashCode();
        }
        #endregion


        #region Conversion methods

        /// <summary>
		/// Creates a list of characters from a string
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static Cons FromString(string value) 
		{
            if (value == null)
                return null;

			Object cons = null;
            for (int i = value.Length -1; i >= 0; i--)
            {
                cons = new Cons(value[i], cons);
            }
			return (Cons) cons;	
		}

		/// <summary>
		/// Creates a list from a one dimensional array. This is recursive,
        /// so that arrays of arrays result in nested lists
		/// </summary>
		/// <param name="array"></param>
		/// <returns></returns>
		public static Cons FromArray(Array array) 
		{
            return FromArrayHelper(array, false);
		}

        /// <summary>
        /// Like FromArray, but keeps sub-arrays as arrays
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static Cons FromShallowArray (Array array)
        {
            return FromArrayHelper(array, true);
        }

		/// <summary>
		/// Creates a list given a DataRow
		/// </summary>
		/// <param name="dataRow"></param>
		/// <returns></returns>
		public static Cons FromDataRow (DataRow dataRow) 
		{
            if (dataRow == null)
                return null;

			Cons list = null;
            if (dataRow != null)
            {
                foreach (object field in dataRow.ItemArray)
                    list = new Cons(field, list);
            }
			return list.NReverse();
		}

		/// <summary>
		/// Creates a list given a DataTable
		/// </summary>
		/// <param name="dataTable"></param>
		/// <returns></returns>
		public static Cons FromDataTable (DataTable dataTable) 
		{
            if (dataTable == null)
                return null;

			Cons list = null;
            if (dataTable != null)
            {
                foreach (DataRow row in dataTable.Rows)
                    list = new Cons(FromDataRow(row), list);
            }
			return list.NReverse();
		}

		/// <summary>
		/// Creates a list given a Hashtable
		/// </summary>
		/// <param name="hashtable"></param>
		/// <returns></returns>
		public static Cons FromHashtable(Hashtable hashtable) 
		{
            if (hashtable == null)
                return null;

			Cons list = null;
            if (hashtable != null)
            {
                foreach (object key in hashtable.Keys)
                {
                    object value = hashtable[key];
                    list = new Cons(new Cons(key, value), list);
                }
            }
			return list;
		}

		/// <summary>
		/// Creates a list from a SortedList
		/// </summary>
		/// <param name="sortedList"></param>
		/// <returns></returns>
		public static Cons FromSortedList(SortedList sortedList) 
		{
            if (sortedList == null)
                return null;

			Cons list = null;
            if (sortedList != null)
            {
                foreach (object key in sortedList.Keys)
                {
                    object value = sortedList[key];
                    list = new Cons(new Cons(key, value), list);
                }
            }
			return list;
		}

		/// <summary>
		/// Creates a list from any ICollection
		/// </summary>
		/// <param name="collection"></param>
		/// <returns></returns>
		public static Cons FromICollection (ICollection collection) 
		{
            return FromIEnumerable(collection as IEnumerable);
		}


        /// <summary>
        /// Create a list from any IEnumerable
        /// </summary>
        /// <param name="array">Any IEnumerable instance</param>
        /// <returns>A cons node</returns>
        public static Cons FromIEnumerable (IEnumerable array)
        {
            if (array == null)
                return null;

            if (array is Cons)
                return (Cons)array;

            Cons list = null;
            foreach (object o in array)
            {
                list = new Cons(o, list);
            }
            return list.NReverse(); 
        }

		/// <summary>
		/// Returns the list expressed as an array; recursive, so 
        /// sublists are converted to arrays, as well
		/// </summary>
		/// <returns></returns>
        public Array ToArray ()
        {
            return ToArrayHelper(false, false);
        }

        /// <summary>
        /// Returns the list expressed as an array; not recursive,
        /// so sublists are keps as lists
        /// </summary>
        /// <returns></returns>
        public Array ToShallowArray()
        {
            return ToArrayHelper(true, false);
        }

        /// <summary>
        /// Returns the list expressed as an explicit array of objects; recursive, so 
        /// sublists are converted to arrays, as well
        /// </summary>
        /// <returns></returns>
        public Object[] ToObjectArray ()
        {
            return (Object[])ToArrayHelper(false, true);
        }

        /// <summary>
        /// Returns the list expressed as an explicit array of objects; not recursive,
        /// so sublists are keps as lists
        /// </summary>
        /// <returns></returns>
        public Object[] ToShallowObjectArray ()
        {
            return (Object[])ToArrayHelper(true, true);
        }

        /// <summary>
        /// Returns the list as a multi-dimensional array
        /// </summary>
        /// <param name="rank">The rank of the array</param>
        /// <returns>A multi-dimensional array array</returns>
        public object ToMdArray(int rank)
        {
            int[] dims = new int[rank];
            int ix = 0;

            object list = this;
            while (list != null && (list is Cons) && ix < rank)
            {
                dims[ix++] = ((Cons)list).Length;
                list = ((Cons)list).Car();
            }

            if (ix != rank)
                throw new YarrException("ToMdArray: Incorrect array rank");

            Type arrType = ArrayType(this, 1, rank);

            Array arr = Array.CreateInstance(arrType, dims);

            int[] ind = new int[rank];
            PopulateMdArray(arr, ind, dims);
            return arr;
        }

        #endregion


        #region Helper methods

        private static Cons FromArrayHelper(Array array, bool shallowCopy)
        {
            if (array == null)
                return null;

            Cons list = null;
            for (int i = array.Length - 1; i >= 0; i--)
            {
                object o = array.GetValue(i);
                list = new Cons((!shallowCopy && (o is Array)) ? FromArrayHelper((Array)o, false) : o, list);
            }
            return (Cons)list;
        }

        private Array ToArrayHelper(bool shallowCopy, bool objectArray)
        {
            int arrayLen = this.Length;
            object[] temp = new object[arrayLen];

            Cons o = this;
            int i = 0;

            foreach (object x in o)
                temp[i++] = (!shallowCopy && (x is Cons)) ? ((Cons)x).ToArrayHelper(false, objectArray) : x;

            // Caller explicitly wants an array of objects, so return what we have ...
            if (objectArray)
                return temp;

            // Try to determine the type of the array
            Type arrayType = typeof(object);
            if (arrayLen > 0)
            {
                arrayType = Primitives.TypeOf(temp[0]);
                for (i = 1; i < arrayLen; ++i)
                    if (Primitives.TypeOf(temp[i]) != arrayType)
                    {
                        arrayType = typeof(object);
                        break;
                    }
            }

            Array arr = Array.CreateInstance(arrayType, arrayLen);
            for (i = 0; i < arrayLen; ++i)
                arr.SetValue(temp[i], i);

            return arr;
		}

        private void PopulateMdArray (Array arr, int[] ind, int[] dims)
        {
            try
            {
                do
                {
                    object o = this;
                    for (int ix = 0; ix < ind.Length; ++ix)
                        o = ((Cons)o).Nth(ind[ix]);

                    arr.SetValue(o, ind);
                }
                while (IncrementIndex(ind.Length - 1, ind, dims));
            }
            catch (Exception e)
            {
                throw new YarrException("PopulateMdArray: Incorrect array rank", e);
            }
        }

        private bool IncrementIndex (int ix, int[] ind, int[] dims)
        {
            bool retval = false;
            if (ix >= 0)
            {
                if (++ind[ix] < dims[ix])
                    retval = true;
                else
                {
                    ind[ix] = 0;
                    retval = IncrementIndex(ix - 1, ind, dims);
                }
            }
            return retval;
        }

        private Type ArrayType (Cons list, int depth, int rank)
        {
            Type arrType = typeof(object);
            if (list.Length > 0)
            {
                if (depth == rank)
                {
                    arrType = Primitives.TypeOf(list.First());
                    foreach (object o in list)
                        if (Primitives.TypeOf(o) != arrType)
                        {
                            arrType = typeof(object);
                            break;
                        }
                }
                else
                {
                    try
                    {
                        arrType = ArrayType((Cons)list.First(), depth + 1, rank);
                        for (int ix = 1; ix < list.Length; ++ix)
                            if (ArrayType((Cons)list.Nth(ix), depth + 1, rank) != arrType)
                            {
                                arrType = typeof(object);
                                break;
                            }
                    }
                    catch (Exception e)
                    {
                        throw new YarrException("ArrayType: Incorrect array rank", e);
                    }
                }
            }

            return arrType;
        }

        #endregion
    }
}
